package com.ihr360.payroll.controller.attendanceplan;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.annonation.RestControl;
import com.ihr360.authority.sharing.privilege.AccessControlMethod;
import com.ihr360.authority.sharing.privilege.SearchPrivilege;
import com.ihr360.authority.sharing.util.AccessControlUtils;
import com.ihr360.commons.entity.AuthUserInfo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.type.WorkHourType;
import com.ihr360.commons.request.Ihr360SearchBody;
import com.ihr360.commons.utils.RestToReturnUtils;
import com.ihr360.maven.plugin.ModuleCodeStore;
import com.ihr360.maven.plugin.annotation.FunctionDefinition;
import com.ihr360.maven.plugin.annotation.ModuleDefinition;
import com.ihr360.payroll.model.dto.attendance.plan.AttendancePlan;
import com.ihr360.payroll.model.dto.attendance.rule.enroll.EnrollAdjustRule;
import com.ihr360.payroll.model.dto.attendance.rule.overtime.OvertimeRule;
import com.ihr360.payroll.model.dto.attendance.rule.personalleave.PersonalLeaveRule;
import com.ihr360.payroll.model.dto.attendance.rule.sickleave.SickLeaveRule;
import com.ihr360.payroll.model.dto.attendance.template.WorkHourTemplate;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.service.attendanceplan.AttendancePlanService;
import com.ihr360.payroll.service.attendanceplan.EnrollAdjustRuleService;
import com.ihr360.payroll.service.attendanceplan.OvertimeRuleService;
import com.ihr360.payroll.service.attendanceplan.PersonalLeaveRuleService;
import com.ihr360.payroll.service.attendanceplan.SickLeaveRuleService;
import com.ihr360.payroll.service.attendanceplan.WorkHourTemplateService;
import com.ihr360.rest.service.SimpleRestService;
import com.ihr360.rest.webmvc.AbstractRestSimpleWithLongIdController;
import com.ihr360.validation.ValidationUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author tomtao
 */
@Api(value = "考勤记薪方案相关接口")
@RestController
@RequestMapping("/attendancePlans")
@ModuleDefinition(mainModuleCode = ModuleCodeStore.Module.SALARY_CODE,
        mainModuleName = ModuleCodeStore.Module.SALARY_NAME,
        subModuleCode = ModuleCodeStore.Module.Salary.SALARY_SETTING_CODE,
        subModuleName = ModuleCodeStore.Module.Salary.SALARY_SETTING_NAME,
        hasExtendsAbstractRestController = true)
@RestControl(controls = {
        AccessControlMethod.METHODS.batchDelete,
        AccessControlMethod.METHODS.deleteOne,
        AccessControlMethod.METHODS.create,
        AccessControlMethod.METHODS.getAll,
        AccessControlMethod.METHODS.getAllInCompany,
        AccessControlMethod.METHODS.update})
public class AttendancePlanController extends AbstractRestSimpleWithLongIdController<AttendancePlan> {

    private static final Logger logger = LoggerFactory.getLogger(AttendancePlanController.class);

    @Autowired
    private AttendancePlanService attendancePlanService;
    @Autowired
    private WorkHourTemplateService workHourTemplateService;
    @Autowired
    private EnrollAdjustRuleService enrollAdjustRuleService;
    @Autowired
    private OvertimeRuleService overtimeRuleService;
    @Autowired
    private PersonalLeaveRuleService personalLeaveRuleService;
    @Autowired
    private SickLeaveRuleService sickLeaveRuleService;

    @Override
    public SimpleRestService<AttendancePlan, Long> getSimpleRestService() {
        return attendancePlanService;
    }

    @FunctionDefinition(functionName = "获取考勤记薪方案list", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "获取考勤记薪方案list")
    @GetMapping(value = "/list", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public List<AttendancePlan> list() {
        return attendancePlanService.list();
    }

    @FunctionDefinition(functionName = "获取帐期内的方案提示", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @GetMapping(value = "/checkIsPayPeriod/{id}")
    public void checkIsPayPeriod(@PathVariable Long id, AuthUserInfo userInfo) {
        List<SalaryPlan> salaryPlanList = attendancePlanService.getIsPayPeriodSalaryPlanList(userInfo.getCompanyId(), id);
        if (!CollectionUtils.isEmpty(salaryPlanList)) {
            StringBuilder salaryPlanName = new StringBuilder();
            int listSize = salaryPlanList.size();
            int index = 1;
            for (SalaryPlan salaryPlan : salaryPlanList) {
                salaryPlanName.append(salaryPlan.getName()).append(index == listSize ? "" : ",");
                index++;
            }
            ValidationUtils.reject("", "考勤计薪规则调整提示：　调整该考勤计薪规则，可能导致核算中的（" + salaryPlanName.toString() + "）方案结果不正确，确定要继续吗？");
        }
        RestToReturnUtils.setMessage("成功！");
    }

    @FunctionDefinition(functionName = "考勤记薪方案名称是否存在", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "考勤记薪方案名称是否存在")
    @GetMapping(value = "/exist", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public boolean exist(@RequestParam String name, @RequestParam Long id) {
        return attendancePlanService.isDuplicateName(name, id);
    }

    @FunctionDefinition(functionName = "获取工时间模板", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "获取工时间模板")
    @GetMapping(value = "/getWorkHourTemplate", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public JSONObject getWorkHourTemplate() {
        JSONObject result = new JSONObject();
        List<WorkHourTemplate> templateList = workHourTemplateService.getAll();
        for (WorkHourTemplate template : templateList) {
            if (WorkHourType.STANDARD.equals(template.getType())) {
                result.put("standard", template);
            } else {
                result.put("synthetical", template);
            }
        }
        return result;
    }

    @FunctionDefinition(functionName = "删除", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @DeleteMapping(value = "/delete/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public boolean deleteById(@PathVariable Long id) {
        if (attendancePlanService.isReferenced(id, false)) {
            ValidationUtils.reject("", "被引用不能删除！");
        }
        try {
            return attendancePlanService.deleteById(id);
        } catch (Ihr360Exception e) {
            throw e;
        } catch (DataIntegrityViolationException e) {
            logger.warn(e.getMessage(), e);
            throw new Ihr360Exception(null, "要删除的数据已被其他数据关联，无法删除");
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
            throw new Ihr360Exception(null, "无法删除数据，请刷新后重试");
        }

    }

    @FunctionDefinition(functionName = "获取一个考勤计薪资方案", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "获取一个考勤计薪资方案")
    @Override
    public ResponseEntity get(@PathVariable(name = "id") Long id, @RequestParam(name = "projection", required = false) String projection) {
        AttendancePlan attendancePlan = attendancePlanService.getOneContainSystemTemplate(id);
        return toResource(attendancePlan, projection);
    }

    @FunctionDefinition(functionName = "修改工时类型", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "改工时类型")
    @PostMapping(value = "/changeWorkHourType/{planId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public boolean changeWorkHourType(@PathVariable Long planId, @RequestBody WorkHourType type) {
        return attendancePlanService.changeWorkHourType(planId, type);
    }

    @FunctionDefinition(functionName = "保存考勤计薪资规则", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "保存考勤计薪资规则")
    @PutMapping(value = {"/save"}, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void save(@RequestBody AttendancePlan entity, AuthUserInfo userInfo) {
        ValidationUtils.rejectIfEmpty(entity.getName(), "", "方案名称不能为空！");
        if (attendancePlanService.isDuplicateName(entity.getName(), entity.getId())) {
            ValidationUtils.reject("", "方案名称\"" + entity.getName() + "\"已存在！");
        }
        entity.setCompanyId(userInfo.getCompanyId());
        entity.setUpdatedAt(new Date());
        attendancePlanService.save(entity);
        RestToReturnUtils.setMessage("保存成功！");
    }

    @FunctionDefinition(functionName = "用户查询考勤计薪规则", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @ApiOperation(value = "用户查询考勤计薪规则")
    @Override
    public ResponseEntity executeSearch(@RequestBody Ihr360SearchBody<AttendancePlan> searchBody,
                                        @RequestParam(name = "projection", required = false) String projection) {
        super.setCompanyId(searchBody);
        SearchPrivilege searchPrivilege = new SearchPrivilege<>(
                super.getAuthUserResourceService(), super.getiAccessControlClass(), searchBody).doValidate();
        boolean isNoData = searchPrivilege.isNoData();
        List<Long> systemTemplateIdList = attendancePlanService.getSystemTemplateIdList();
        if (!CollectionUtils.isEmpty(systemTemplateIdList)) {
            isNoData = false;
        }
        Specification<AttendancePlan> specification = (root, cq, cb) -> {
            List<Predicate> predicates = Lists.newArrayList();
            Predicate predicate = searchBody.getSpecification().toPredicate(root, cq, cb);
            if (predicate != null) {
                predicates.add(predicate);
            }
            if (!CollectionUtils.isEmpty(systemTemplateIdList)) {
                CriteriaBuilder.In<Long> systemTemplateIdIn = cb.in(root.get(AttendancePlan.Field.ID).as(Long.class));
                systemTemplateIdList.forEach(systemTemplateIdIn::value);
                predicates.add(systemTemplateIdIn);
            }
            return cb.or((Predicate[]) predicates.toArray(new Predicate[predicates.size()]));
        };
        List<String> sortList = new ArrayList<>();
        String[] sort = searchBody.getSort();
        if (sort != null) {
            Collections.addAll(sortList, sort);
        } else {
            sortList.add(AttendancePlan.Field.IS_SYSTEM_TEMPLATE + ",DESC");
            sortList.add(AttendancePlan.Field.NAME + ",DESC");
        }
        searchBody.setSort(sortList.toArray(new String[sortList.size()]));
        Page<AttendancePlan> page = attendancePlanService.search(specification, searchBody.getPageable(), isNoData);

        List<AttendancePlan> content = page.getContent();
        Map<Long, Integer> privilegeMap = searchPrivilege.getUserResourcesPrivilege();
        Map<Long, Integer> publicityMap = searchPrivilege.getUserResourcesPublicity();
        content.forEach(s -> AccessControlUtils.setAccessControl2Target(s, privilegeMap, publicityMap));
        return toResource(page, projection);
    }

    @FunctionDefinition(functionName = "保存或更新入离职调整规则", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @PostMapping(value = "/enrollAdjustRules/save")
    public boolean saveEnrollAdjustRules(@RequestBody EnrollAdjustRule enrollAdjustRule) {
        enrollAdjustRuleService.save(enrollAdjustRule);
        return true;
    }

    @FunctionDefinition(functionName = "保存或更新加班规则", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @PostMapping(value = "/overtimeRules/save")
    public boolean saveOvertimeRules(@RequestBody OvertimeRule overtimeRule) {
        overtimeRuleService.save(overtimeRule);
        return true;
    }

    @FunctionDefinition(functionName = "保存或更新事假扣款规则", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @PostMapping(value = "/personalLeaveRules/save")
    public boolean savePersonalLeaveRules(@RequestBody PersonalLeaveRule personalLeaveRule) {
        personalLeaveRuleService.save(personalLeaveRule);
        return true;
    }

    @FunctionDefinition(functionName = "保存或更新病假扣款", functionCode = ModuleCodeStore.Function.ATTENDANCE_PLAN)
    @PostMapping(value = "/sickLeaveRules/save")
    public boolean saveSickLeaveRules(@RequestBody SickLeaveRule sickLeaveRule) {
        sickLeaveRuleService.save(sickLeaveRule);
        return true;
    }


}
